import { join } from 'path'
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs'
import { BrowserWindow, app, ipcMain, screen, Tray, Menu } from 'electron'
import Transpoter from './transpoter'

declare type GameOption = {
  name: string,
  type: 'TCP' | 'UDP',
  port: number,
  datas: {
    offset: number,
    name: string,
    type: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64',
    rindex: number,
    rtype: 'int8' | 'uint8' | 'int16' | 'uint16' | 'int32' | 'uint32' | 'int64' | 'uint64' | 'float32' | 'float64',
    fn: string
  }[]
}

let mainWindow!: BrowserWindow
const savePath = join(app.getPath('documents'), 'RallyTools')
let savedGames = [] as any[]
let savedPages = [] as any[]

try {
  // 加载保存的记录
  if (!existsSync(savePath)) {
    mkdirSync(savePath)
  }
  const games = JSON.parse(readFileSync(join(savePath, 'games.json'), { encoding: 'utf-8' }))
  savedGames = [...games]
} catch {
  savedGames = []
}

try {
  // 加载保存的记录
  if (!existsSync(join(savePath, 'pages'))) {
    mkdirSync(join(savePath, 'pages'))
  }
  const dirs = readdirSync(join(savePath, 'pages'))
  for (let n of dirs) {
    if (!/[A-Za-z0-9\-_]+/.test(n)) {
      continue
    }
    try {
      if (!existsSync(join(savePath, 'pages', n, 'index.html'))) {
        continue
      }
      const page = JSON.parse(readFileSync(join(savePath, 'pages', n, 'package.json'), { encoding: 'utf-8' }))
      savedPages.push({
        id: n,
        name: page.name,
        url: '/pages/' + n + '/index.html',
        width: page.width,
        height: page.height,
        placement: page.placement
      })
    } catch {
      //
    }
  }
} catch {
  savedPages = []
}
savedPages.push({
  id: 'default-page',
  name: '内置页面1',
  url: '/dash',
  width: 1280,
  height: 240,
  placement: 'bottom-center'
})

function createWindow() {
  const win = new BrowserWindow({
    width: 1280,
    height: 720,
    resizable: false,
    frame: false,
    transparent: true,
    webPreferences: {
      nodeIntegration: true,
      webSecurity: false,
      contextIsolation: false,
      sandbox: false
    }
  })

  if (process.argv[2] === 'debug') {
    win.loadURL('http://localhost:4567')
    win.webContents.openDevTools()
  } else {
    win.loadFile('./dist/index.html')
  }

  win.once('closed', () => {
    app.exit(0)
  })

  mainWindow = win

  const tray = new Tray(join(__dirname, '/dist/favicon.ico'))
  tray.setToolTip('双击显示主界面')
  const contextMenu = Menu.buildFromTemplate([
    {
      label: '显示', type: 'normal', click() {
        win.show()
      }
    },
    {
      label: '退出', type: 'normal', click() {
        app.exit()
      }
    }
  ])
  tray.setContextMenu(contextMenu)
  tray.on('double-click', () => {
    win.show()
  })
}

app.whenReady().then(createWindow)

app.once('window-all-closed', () => {
  app.exit(0)
})

ipcMain.handle('fetch-all', async () => {
  return savedGames
})

ipcMain.handle('fetch-all-pages', async () => {
  return savedPages
})

ipcMain.handle('add', async (evt, name: string) => {
  if (savedGames.find(v => v.name === name)) {
    return null
  }
  try {
    savedGames.push({
      name,
      type: 'UDP',
      port: 5566,
      datas: [
        {
          offset: 0,
          name: '游戏状态',
          type: 'uint8',
          rindex: 0,
          rtype: 'int32',
          fn: ''
        },
        {
          offset: 1,
          name: '车辆类型',
          type: 'uint8',
          rindex: 0,
          rtype: 'int32',
          fn: ''
        },
        {
          offset: 2,
          name: '挡位',
          type: 'int8',
          rindex: 0,
          rtype: 'int32',
          fn: ''
        },
        {
          offset: 3,
          name: '最大挡位',
          type: 'uint8',
          rindex: 0,
          rtype: 'int32',
          fn: ''
        },
        {
          offset: 4,
          name: '方向盘位置',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 8,
          name: '油门位置',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 12,
          name: '制动位置',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 16,
          name: '离合位置',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 20,
          name: '当前时速',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 24,
          name: '引擎空转速度',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 28,
          name: '引擎转速',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 32,
          name: '最大引擎转速',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 36,
          name: '侧向G力',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 40,
          name: '前后G力',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 44,
          name: '侧向角度',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 48,
          name: '前后角度',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 52,
          name: '前左减震变形比例',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 56,
          name: '前右减震变形比例',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 60,
          name: '后左减震变形比例',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 64,
          name: '后右减震变形比例',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 68,
          name: '前左减震变形量',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 72,
          name: '前右减震变形量',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 76,
          name: '后左减震变形量',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        },
        {
          offset: 80,
          name: '后右减震变形量',
          type: 'float32',
          rindex: 0,
          rtype: 'float32',
          fn: ''
        }
      ]
    })
    writeFileSync(join(savePath, 'games.json'), JSON.stringify(savedGames), { encoding: 'utf-8' })
    return savedGames
  } catch {
    return null
  }
})

ipcMain.handle('modify', async (evt, args: string) => {
  try {
    const game = JSON.parse(args) as GameOption
    const index = savedGames.findIndex(v => v.name === game.name)
    if (index < 0) {
      return false
    }
    savedGames.splice(index, 1, game)
    writeFileSync(join(savePath, 'games.json'), JSON.stringify(savedGames), { encoding: 'utf-8' })
    return true
  } catch {
    return false
  }
})

ipcMain.handle('remove', async (evt, name: string) => {
  try {
    const index = savedGames.findIndex(v => v.name === name)
    if (index < 0) {
      return false
    }
    savedGames.splice(index, 1)
    writeFileSync(join(savePath, 'games.json'), JSON.stringify(savedGames), { encoding: 'utf-8' })
    return true
  } catch {
    return false
  }
})

ipcMain.handle('mini', () => {
  // mainWindow.minimize()
  mainWindow.hide()
})

let dashWindow: null | {
  game: string,
  page: string,
  win: BrowserWindow,
  transpoter: {
    on(evt: string, cb: (data: Buffer) => void): void,
    destroy(): void
  } | null
} = null

ipcMain.handle('start', async (_: any, args: string) => {
  if (dashWindow) {
    return false
  }
  const { game, page } = JSON.parse(args) as {
    game: string, page: {
      id: string,
      name: string,
      url: string,
      width: number,
      height: number,
      placement: string
    }
  }
  const gameOption = savedGames.find(v => v.name === game)
  if (!gameOption) {
    return false
  }
  const { x, y, width, height } = screen.getAllDisplays()[0].bounds
  const win = new BrowserWindow({
    width: page.width,
    height: page.height,
    movable: true,
    resizable: false,
    frame: false,
    transparent: true,
    alwaysOnTop: true,
    skipTaskbar: false,
    x: x + width / 2 - page.width / 2,
    y: y + height - page.height,
    webPreferences: {
      nodeIntegration: true,
      webSecurity: false,
      contextIsolation: false
    }
  })
  const WM_INITMENU = 0x0116;
  win.hookWindowMessage(WM_INITMENU, () => {
    win.setEnabled(false);
    win.setEnabled(true);
  })
  win.webContents.insertCSS(`body {
    user-select: none !important;
    -webkit-app-region: drag !important;
  }`)
  if (process.argv[2] === 'debug' && !page.url.includes('/pages/')) {
    win.loadURL('http://localhost:4567/#' + page.url)
  } else {
    if (page.url.includes('/pages/')) {
      win.loadFile(join(savePath, page.url))
    } else {
      win.loadFile('dist/index.html', {
        hash: '#' + page.url
      })
    }
  }
  if (process.argv[2] === 'debug') {
    win.webContents.openDevTools()
  }
  dashWindow = {
    game,
    page: page.id,
    win,
    transpoter: null
  }

  dashWindow.transpoter = new Transpoter(gameOption) as any
  dashWindow.transpoter?.on('data', data => {
    win.webContents.postMessage('data', data)
  })
  dashWindow.win.once('closed', () => {
    mainWindow?.webContents?.postMessage('state-stop', null)
    dashWindow?.transpoter?.destroy()
    dashWindow = null as any
  })
  mainWindow.hide()
  return true
})

ipcMain.on('stop', () => {
  dashWindow?.win?.close()
  dashWindow?.transpoter?.destroy()
  dashWindow = null as any
})
